// Copyright 2017 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT

#include <arch/asm_macros.h>
#include <asm.h>
#include <zircon/errors.h>


// For details please refer to ARM Generic Interrupt Controller Architecture Specification,
// GIC architecture version 3.0 and version 4.0. Table 8-6 Mapping of MSR and MRS to
// virtual interface control registers, AArch64 state and Table 8-8 Mapping of MCR and
// MRC to virtual interface control registers, AArch32 state.
#define ICH_AP0R0_EL2                   S3_4_C12_C8_0
#define ICH_HCR_EL2                     S3_4_C12_C11_0
#define ICH_VTR_EL2                     S3_4_C12_C11_1
#define ICH_MISR_EL2                    S3_4_C12_C11_2
#define ICH_ELRSR_EL2                   S3_4_C12_C11_5
#define ICH_VMCR_EL2                    S3_4_C12_C11_7

#define ICH_AP0R0_EL2_ID                0
#define ICH_HCR_EL2_ID                  1
#define ICH_VTR_EL2_ID                  2
#define ICH_MISR_EL2_ID                 3
#define ICH_ELRSR_EL2_ID                4
#define ICH_VMCR_EL2_ID                 5
#define ICH_LR_EL2_ID                   6

#define LR0_EL2(x)                      S3_4_C12_C12_ ## x
#define LR8_EL2(x)                      S3_4_C12_C13_ ## x

#define ICH_LR0                         LR0_EL2(0)
#define ICH_LR1                         LR0_EL2(1)
#define ICH_LR2                         LR0_EL2(2)
#define ICH_LR3                         LR0_EL2(3)
#define ICH_LR4                         LR0_EL2(4)
#define ICH_LR5                         LR0_EL2(5)
#define ICH_LR6                         LR0_EL2(6)
#define ICH_LR7                         LR0_EL2(7)
#define ICH_LR8                         LR8_EL2(0)
#define ICH_LR9                         LR8_EL2(1)
#define ICH_LR10                        LR8_EL2(2)
#define ICH_LR11                        LR8_EL2(3)
#define ICH_LR12                        LR8_EL2(4)
#define ICH_LR13                        LR8_EL2(5)
#define ICH_LR14                        LR8_EL2(6)
#define ICH_LR15                        LR8_EL2(7)

#define ICH_LR(x)                       ICH_LR ## x

#define READ_SYSREG_ID            0
#define WRITE_SYSREG_ID           1

.section .text.el2,"ax",@progbits
.align 12

.macro read_sysreg literal
    mrs x0, \literal
    b el2_gicv3_done
.endm

.macro write_sysreg literal
    msr \literal, x0
    b el2_gicv3_done
.endm

.macro invalid_write
    b el2_gicv3_done
    nop
.endm

// Branch to an address within a jump table, calculated as follows:
//   address = table + (reg << 3)
.macro gic_jump table reg
    adr x9, \table
    add x9, x9, \reg, lsl 3
    br x9
.endm

// x0 - Value to write
// x1 - Index for ICH register
// x2 - READ/WRITE
// x3 - Register name/enum
FUNCTION_LABEL(el2_hvc_sysreg)
    cbnz x2, .write_sys

    gic_jump .Lread_sys x3

.Lread_sys:
    read_sysreg ICH_AP0R0_EL2
    read_sysreg ICH_HCR_EL2
    read_sysreg ICH_VTR_EL2
    read_sysreg ICH_MISR_EL2
    read_sysreg ICH_ELRSR_EL2
    read_sysreg ICH_VMCR_EL2
    b el2_gicv3_read_lr

.write_sys:
    gic_jump .Lwrite_sys x3

.Lwrite_sys:
    write_sysreg ICH_AP0R0_EL2
    write_sysreg ICH_HCR_EL2
    invalid_write // ICH_VTR_EL2 is readonly
    invalid_write // ICH_MISR_EL2 is readonly
    invalid_write // ICH_ELRSR_EL2 is readonly
    write_sysreg ICH_VMCR_EL2
    b el2_gicv3_write_lr

// uint32_t arm64_el2_gicv3_read_gich_apr();
FUNCTION(arm64_el2_gicv3_read_gich_apr)
    mov x2, READ_SYSREG_ID
    mov x3, ICH_AP0R0_EL2_ID
    hvc 5
    ret
END_FUNCTION(arm64_el2_gicv3_read_gich_apr)

// void arm64_el2_gicv3_write_gich_apr(uint32_t val)
FUNCTION(arm64_el2_gicv3_write_gich_apr)
    mov x2, WRITE_SYSREG_ID
    mov x3, ICH_AP0R0_EL2_ID
    hvc 5
    ret
END_FUNCTION(arm64_el2_gicv3_write_gich_apr)

// uint32_t arm64_el2_gicv3_read_gich_hcr();
FUNCTION(arm64_el2_gicv3_read_gich_hcr)
    mov x2, READ_SYSREG_ID
    mov x3, ICH_HCR_EL2_ID
    hvc 5
    ret
END_FUNCTION(arm64_el2_gicv3_read_gich_hcr)

// void arm64_el2_gicv3_write_gich_hcr(uint32_t val)
FUNCTION(arm64_el2_gicv3_write_gich_hcr)
    mov x2, WRITE_SYSREG_ID
    mov x3, ICH_HCR_EL2_ID
    hvc 5
    ret
END_FUNCTION(arm64_el2_gicv3_write_gich_hcr)

// uint32_t arm64_el2_gicv3_read_gich_vtr();
FUNCTION(arm64_el2_gicv3_read_gich_vtr)
    mov x2, READ_SYSREG_ID
    mov x3, ICH_VTR_EL2_ID
    hvc 5
    ret
END_FUNCTION(arm64_el2_gicv3_read_gich_vtr)

// uint32_t arm64_el2_gicv3_read_gich_vmcr();
FUNCTION(arm64_el2_gicv3_read_gich_vmcr)
    mov x2, READ_SYSREG_ID
    mov x3, ICH_VMCR_EL2_ID
    hvc 5
    ret
END_FUNCTION(arm64_el2_gicv3_read_gich_vmcr)

// void arm64_el2_gicv3_write_gich_vmcr(uint32_t val)
FUNCTION(arm64_el2_gicv3_write_gich_vmcr)
    mov x2, WRITE_SYSREG_ID
    mov x3, ICH_VMCR_EL2_ID
    hvc 5
    ret
END_FUNCTION(arm64_el2_gicv3_write_gich_vmcr)

// uint32_t arm64_el2_gicv3_read_gich_elrsr();
FUNCTION(arm64_el2_gicv3_read_gich_elrsr)
    mov x2, READ_SYSREG_ID
    mov x3, ICH_ELRSR_EL2_ID
    hvc 5
    ret
END_FUNCTION(arm64_el2_gicv3_read_gich_elrsr)

// uint32_t arm64_el2_gicv3_read_gich_misr();
FUNCTION(arm64_el2_gicv3_read_gich_misr)
    mov x2, READ_SYSREG_ID
    mov x3, ICH_MISR_EL2_ID
    hvc 5
    ret
END_FUNCTION(arm64_el2_gicv3_read_gich_misr)

// uint64_t arm64_el2_gicv3_read_gich_lr(uint32_t index);
FUNCTION(arm64_el2_gicv3_read_gich_lr)
    mov x1, x0
    mov x2, READ_SYSREG_ID
    mov x3, ICH_LR_EL2_ID
    hvc 5
    ret
END_FUNCTION(arm64_el2_gicv3_read_gich_lr)

// void arm64_el2_gicv3_write_gich_lr(uint64_t val, uint32_t index)
FUNCTION(arm64_el2_gicv3_write_gich_lr)
    mov x2, WRITE_SYSREG_ID
    mov x3, ICH_LR_EL2_ID
    hvc 5
    ret
END_FUNCTION(arm64_el2_gicv3_write_gich_lr)

// x1 - Index for LR register
FUNCTION_LABEL(el2_gicv3_read_lr)
    gic_jump .Llr_read_table x1

.Llr_read_table:
    read_sysreg ICH_LR(0)
    read_sysreg ICH_LR(1)
    read_sysreg ICH_LR(2)
    read_sysreg ICH_LR(3)
    read_sysreg ICH_LR(4)
    read_sysreg ICH_LR(5)
    read_sysreg ICH_LR(6)
    read_sysreg ICH_LR(7)
    read_sysreg ICH_LR(8)
    read_sysreg ICH_LR(9)
    read_sysreg ICH_LR(10)
    read_sysreg ICH_LR(11)
    read_sysreg ICH_LR(12)
    read_sysreg ICH_LR(13)
    read_sysreg ICH_LR(14)
    read_sysreg ICH_LR(15)

// x0 - Value to write
// x1 - Index for LR register
FUNCTION_LABEL(el2_gicv3_write_lr)
    gic_jump .Llr_write_table x1

.Llr_write_table:
    write_sysreg ICH_LR(0)
    write_sysreg ICH_LR(1)
    write_sysreg ICH_LR(2)
    write_sysreg ICH_LR(3)
    write_sysreg ICH_LR(4)
    write_sysreg ICH_LR(5)
    write_sysreg ICH_LR(6)
    write_sysreg ICH_LR(7)
    write_sysreg ICH_LR(8)
    write_sysreg ICH_LR(9)
    write_sysreg ICH_LR(10)
    write_sysreg ICH_LR(11)
    write_sysreg ICH_LR(12)
    write_sysreg ICH_LR(13)
    write_sysreg ICH_LR(14)
    write_sysreg ICH_LR(15)

FUNCTION_LABEL(el2_gicv3_done)
    msr vttbr_el2, xzr
    isb
    eret
